﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace BJoin.Common
{
    public static partial class Extension
    {
        #region foreach async
        /// <summary>
        /// ForEach Async for IEnumerable
        /// </summary>
        /// <typeparam name="T">object</typeparam>
        /// <param name="collection">IEnumerable</param>
        /// <param name="method">func</param>
        /// <returns>Task</returns>
        public static async Task ForEachAsync<T>(this IEnumerable<T> collection, Func<T, Task> method)
        {
            foreach (var value in collection)
            {
                await method(value);
            }
        }
        /// <summary>
        /// ForEach Async for List
        /// </summary>
        /// <typeparam name="T">object</typeparam>
        /// <param name="collection">List</param>
        /// <param name="method">func</param>
        /// <returns>Task</returns>
        public static async Task ForEachAsync<T>(this List<T> collection, Func<T, Task> method)
        {
            foreach (var value in collection)
            {
                await method(value);
            }
        }
        /// <summary>
        /// ForEach Async for IList
        /// </summary>
        /// <typeparam name="T">object</typeparam>
        /// <param name="collection">IList</param>
        /// <param name="method">func</param>
        /// <returns>Task</returns>
        public static async Task ForEachAsync<T>(this IList<T> collection, Func<T, Task> method)
        {
            foreach (var value in collection)
            {
                await method(value);
            }
        }
        #endregion

        #region select async
        /// <summary>
        /// Select Async for IEnumerable
        /// </summary>
        /// <typeparam name="TSource">TSource</typeparam>
        /// <typeparam name="TResult">TResult</typeparam>
        /// <param name="source">source</param>
        /// <param name="method">func</param>
        /// <returns>Task<IEnumerable<TResult>></returns>
        public static async Task<IEnumerable<TResult>> SelectAsync<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, Task<TResult>> method)
        {
            return await Task.WhenAll(source.Select(async s => await method(s))); 
        }
        /// <summary>
        /// Select Async for List
        /// </summary>
        /// <typeparam name="TSource">TSource</typeparam>
        /// <typeparam name="TResult">TResult</typeparam>
        /// <param name="source">source</param>
        /// <param name="method">func</param>
        /// <returns>Task<IEnumerable<TResult>></returns>
        public static async Task<IEnumerable<TResult>> SelectAsync<TSource, TResult>(this List<TSource> source, Func<TSource, Task<TResult>> method)
        {
            return await Task.WhenAll(source.Select(async s => await method(s)));
        }
        /// <summary>
        /// Select Async for IList
        /// </summary>
        /// <typeparam name="TSource">TSource</typeparam>
        /// <typeparam name="TResult">TResult</typeparam>
        /// <param name="source">source</param>
        /// <param name="method">func</param>
        /// <returns>Task<IEnumerable<TResult>></returns>
        public static async Task<IEnumerable<TResult>> SelectAsync<TSource, TResult>(this IList<TSource> source, Func<TSource, Task<TResult>> method)
        {
            return await Task.WhenAll(source.Select(async s => await method(s)));
        }
        #endregion

        #region DistinctBy
        public static IEnumerable<TSource> DistinctBy<TSource, TKey> (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
        {
            HashSet<TKey> seenKeys = new HashSet<TKey>();
            foreach (TSource element in source)
            {
                if (seenKeys.Add(keySelector(element)))
                {
                    yield return element;
                }
            }
        }
        #endregion
    }
}
